 /*
 *   *       Please Visit us at www.codemiles.com     *
 *  This Program was Developed by www.codemiles.com forums Team
 *  *           Please Don't Remove This Comment       *
 */
package clientchat;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.util.*;
import javax.swing.*;
import javax.swing.tree.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
import org.w3c.dom.*;

/**
 *
 * @author Muhamed
 */
/**
 * 
 * Tipos de mensajes:
 * 0: Server
 * 1: Broadcast
 * 2: Unicast
 * 3: Login
 * 4: Logout
 * 5: Welcome
 */
public class MyFrame extends JFrame {

    /** Creates a new instance of myFrame */
    private JTextArea chatBox = new JTextArea();
    private JScrollPane myChatHistory = new JScrollPane(chatBox, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
            JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
    private JTextArea userText = new JTextArea(5, 40);
    private JScrollPane myUserHistory = new JScrollPane(userText, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
            JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
    private JButton send = new JButton("Send");
    private JButton start = new JButton("Connect");
    private Client chatClient;
    private ReadThread myRead = new ReadThread();
    private JTextField server = new JTextField(20);
    private JLabel lblChatHistory = new JLabel("Chat History:");
    private JLabel lblContacts = new JLabel("Contacts:");
    private JLabel myLabel = new JLabel("Server Name :");
    private JLabel lblUser = new JLabel("User Name:");
    private JTextField user = new JTextField(20);
    private String serverName, userName, sType, sUser, sMessage, dest;
    private DefaultMutableTreeNode group, contact;
    private DefaultTreeModel treemodel;
    private JTree tree;
    private JScrollPane treescrollpane;
    private Hashtable nodes;
    private DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    private DocumentBuilder builder;
    private Document document;
    private Element eRoot, eType, eUser, eMessage, eUsersList, eReceiver;
    private TransformerFactory tFactory;
    private Transformer transformer;
    private DOMSource source;
    private StringWriter sw;
    private StreamResult result;

    public MyFrame() {
        setResizable(false);
        setTitle("Client");
        setSize(700, 530);
        Container cp = getContentPane();
        cp.setLayout(null);
        lblChatHistory.setBounds(10, 10, 100, 20);
        cp.add(lblChatHistory);
        myChatHistory.setBounds(10, 40, 500, 300);
        cp.add(myChatHistory);
        //Agregamos un panel para mostrar los usuarios conectados
        lblContacts.setBounds(520, 10, 100, 20);
        group = new DefaultMutableTreeNode("Group");
        treemodel = new DefaultTreeModel(group);
        tree = new JTree(treemodel);
        tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
        tree.setCellRenderer(new DefaultTreeCellRenderer() {

            public Component getTreeCellRendererComponent(JTree tree, Object value,
                    boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
                super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
                if (row == 0) {
                    setIcon(new ImageIcon(getClass().getResource("/imagenes/user_group.png")));
                } else {
                    setIcon(new ImageIcon(getClass().getResource("/imagenes/user.png")));
                    if (sel) {
                        dest = ((DefaultMutableTreeNode) value).getUserObject().toString();
                    }
                }
                return this;
            }
        });
        tree.addMouseListener(new MouseAdapter() {

            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2) {
                    return;
                }
            }
        });
        treescrollpane = new JScrollPane(tree);
        treescrollpane.setBounds(520, 40, 170, 300);
        //Hasta aquí
        cp.add(lblContacts);
        cp.add(treescrollpane);
        myUserHistory.setBounds(10, 350, 500, 100);
        cp.add(myUserHistory);
        send.setBounds(560, 380, 100, 40);
        cp.add(send);
        myLabel.setBounds(10, 460, 100, 20);
        cp.add(myLabel);
        server.setBounds(120, 460, 110, 20);
        cp.add(server);
        lblUser.setBounds(285, 460, 100, 20);
        cp.add(lblUser);
        user.setBounds(395, 460, 115, 20);
        cp.add(user);
        start.setBounds(560, 460, 100, 40);
        cp.add(start);
        send.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                if (chatClient != null) {
                    try {
                        //Tomamos el nombre del usuario, si está vacío tomamos la direccion IP como nombre
                        userName = userName.length() > 0 ? userName : InetAddress.getLocalHost().getHostAddress();
                    } catch (UnknownHostException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    }
                    int[] selected = tree.getSelectionRows();
                    if(selected != null) {
                    	for(int i = 0; i < selected.length; i++) {
                    	    if(selected[i] != 0)
                    	        chatClient.sendPrivateMessage(((DefaultMutableTreeNode) treemodel.getChild(group, selected[i] - 1)).getUserObject().toString(), "2", userText.getText());
                    	    else {
                    	    	//Group esta seleccionado por lo tanto enviamos el mensaje a todo el grupo
                    	    	chatClient.sendMessage("1", userText.getText());
                    	    	break;
                    	    }
                    	}
                    }
                    else {
                        chatClient.sendMessage("1", userText.getText());
                    }
                    chatBox.append(userName + "> " + userText.getText() + "\n");
                }
            }
        });
        start.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                chatClient = new Client();
                chatClient.start();
                //Enviamos el nickname
                chatClient.sendMessage("3", "");
            }
        });
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setVisible(true);
        server.requestFocus();

    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
        new MyFrame();
    }

    public class Client extends Thread {

        private static final int PORT = 9999;
        private LinkedList clients;
        private ByteBuffer readBuffer;
        private ByteBuffer writeBuffer;
        private SocketChannel sChan;
        private Selector readSelector;
        private CharsetDecoder utf8Decoder;

        public Client() {
            clients = new LinkedList();
            nodes = new Hashtable<String, DefaultMutableTreeNode>();
            readBuffer = ByteBuffer.allocateDirect(300);
            writeBuffer = ByteBuffer.allocateDirect(300);
            utf8Decoder = Charset.forName("UTF-8").newDecoder();
        }

        public void run() {

            serverName = server.getText();
            System.out.println(serverName);
            userName = user.getText();

            connect(serverName);
            myRead.start();
            while (true) {

                readMessage();

                try {
                    Thread.sleep(30);
                }
                catch (InterruptedException ie) {
                }
            }

        }

        public void connect(String hostname) {
            try {
                readSelector = Selector.open();
                InetAddress addr = InetAddress.getByName(hostname);
                sChan = SocketChannel.open(new InetSocketAddress(addr, PORT));
                sChan.configureBlocking(false);
                sChan.register(readSelector, SelectionKey.OP_READ, new StringBuffer());
            } catch (Exception e) {
            }
        }

        public void sendMessage(String type, String message) {
            prepareBuffer(createXMLMessage(null, type, message));
            channelWrite(sChan);
        }
        
        public void sendPrivateMessage(String receiver, String type, String message) {
            prepareBuffer(createXMLMessage(receiver, type, message));
            channelWrite(sChan);
        }

        public String createXMLMessage(String receiver, String type, String message) {

            try {
                builder = factory.newDocumentBuilder();
                //Aqui creamos el documento xml y le agregamos los elementos
	            document = builder.newDocument();
	            //Elemento raíz
	            eRoot = document.createElement("root");
	            document.appendChild(eRoot);
	            //Elemento para el tipo de mensaje
	            eType = document.createElement("type");
	            //El 1 significa mensaje broadcast (default)
	            eType.setTextContent(type);
	            eRoot.appendChild(eType);
	            if(type.equals("2")) {
	            	eReceiver = document.createElement("receiver");
	            	eReceiver.setTextContent(receiver);
	            	eRoot.appendChild(eReceiver);
	            }
	            //Si el tipo es 3 entonces enviamos el nombre de usuario
	            else if(type.equals("3")) {
	                //Elemento para el nombre de usuario
	                eUser = document.createElement("user");
	                eUser.setTextContent(user.getText());
	                eRoot.appendChild(eUser);
	            }
	            //Elemento para el mensaje
	            eMessage = document.createElement("message");
	            eMessage.setTextContent(message);
	            eRoot.appendChild(eMessage);
	            document.getDocumentElement().normalize();
	            tFactory = TransformerFactory.newInstance();
	            transformer = tFactory.newTransformer();
	
	            //Preparamos el documento XML para ser analizado con dom y convertirlo a String
	            source = new DOMSource(document);
	            sw = new StringWriter();
	            result = new StreamResult(sw);
	
	            transformer.transform(source, result);
	        }
            catch (Exception e) {
	                e.printStackTrace();
            }
            return sw.toString();
        }

        public void prepareBuffer(String xml) {
            writeBuffer.clear();
            writeBuffer.put(xml.getBytes());
            writeBuffer.flip();
        }

        public void channelWrite(SocketChannel sC) {
            long num = 0;
            long len = writeBuffer.remaining();
            while (num != len) {
                try {
                    num += sChan.write(writeBuffer);

                    Thread.sleep(5);
                } catch (Exception ex) {
                    ex.printStackTrace();
            }
            writeBuffer.rewind();
        }
        }

        public void readMessage() {
            try {
                readSelector.selectNow();
                Set readyKeys = readSelector.selectedKeys();
                Iterator i = readyKeys.iterator();

                while (i.hasNext()) {
                    SelectionKey key = (SelectionKey) i.next();
                    i.remove();
                    SocketChannel channel = (SocketChannel) key.channel();
                    readBuffer.clear();
                    long nbytes = channel.read(readBuffer);

                    if (nbytes == -1) {
                        chatBox.append("The server goes down!\n");
                        channel.close();
                    } else {
                        readBuffer.flip();
                        eRoot = (Element) newDocument(readBuffer).getDocumentElement();
                        eType = (Element) eRoot.getElementsByTagName("type").item(0);
                        sType = eType.getTextContent();
                        //PODRIA RESOLVERSE CON POLIMORFISMO, PERO QUEDARIAN MUCHAS CLASES
                        //DE ACUERDO AL NUMERO DE TIPOS DE MENSAJE
                        if (sType.equals("3")) {
                            eUser = (Element) eRoot.getElementsByTagName("user").item(0);
                            sUser = eUser.getTextContent();
                            //agregamos el nuevo usuario al arbol
                            contact = new DefaultMutableTreeNode(sUser);
                            nodes.put(sUser, contact);
                            group.add(contact);
                            tree.repaint();
                            tree.expandRow(0);
                        }
                        else if(sType.equals("4")) {
                            eUser = (Element) eRoot.getElementsByTagName("user").item(0);
                            sUser = eUser.getTextContent();
                            //eliminamos el usuario del arbol
                            group.remove((DefaultMutableTreeNode) nodes.get(sUser));
                    	    nodes.remove(sUser);
                    	    tree.repaint();
                        	tree.expandRow(0);
                        }
                        else if(!sType.equals("3") && !sType.equals("4")) {
                            eMessage = (Element) eRoot.getElementsByTagName("message").item(0);
                            sMessage = eMessage.getTextContent();

                            if ((sMessage.indexOf("\n") != -1) || (sMessage.indexOf("\r") != -1)) {
                                sMessage = sMessage.trim();
                            }

                            chatBox.append("> " + sMessage + "\n");
                        }
                        
                        if(sType.equals("5")) {
                        	//Si el mensaje es de tipo 5, entonces es de bienvenida y recibimos
                        	//Una lista con todos los usuarios
                        	eUsersList = (Element) eRoot.getElementsByTagName("users-list").item(0);
                        	NodeList contacts = eUsersList.getChildNodes();
                        	for(int index = 0; index < contacts.getLength(); index++) {
                        		Element node = (Element) contacts.item(index);
                                sUser = node.getAttribute("name");
                            	contact = new DefaultMutableTreeNode(sUser);
                                nodes.put(sUser, contact);
                                group.add(contact);
                        	}
                        	tree.repaint();
                        	tree.expandRow(0);
                        }

                    }
                }
            }catch (Exception e) {
            }
        }

        public Document newDocument(ByteBuffer readBuffer) {
            try {
                String xml = utf8Decoder.decode(readBuffer).toString();
                factory = DocumentBuilderFactory.newInstance();
                document = (Document) factory.newDocumentBuilder().parse(new ByteArrayInputStream(xml.getBytes()));

            } catch (Exception e) {
                e.printStackTrace();
            }
            readBuffer.clear();
            return document;
        }
    }

    class ReadThread extends Thread {

        public void run() {
            chatClient.readMessage();
        }
    }
}
 /*
  *   *       Please Visit us at www.codemiles.com     *
  *  This Program was Developed by www.codemiles.com forums Team
  *  *           Please Don't Remove This Comment       *
  */